/**
 * Copyright (c) 2011 ~ 2013 Deepin, Inc.
 *               2011 ~ 2013 jouyouyun
 *
 * Author:      jouyouyun <jouyouwen717@gmail.com>
 * Maintainer:  jouyouyun <jouyouwen717@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 **/

package main

import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "net/http"
        "strconv"
        "strings"
        "time"
)

const (
        MAP_GOOGLE_GEOCODE  = "http://maps.googleapis.com/maps/api/geocode/json?sensor=false"
        MAP_GOOGLE_TIMEZONE = "https://maps.googleapis.com/maps/api/timezone/json?sensor=false&timestamp=1331161200"
)

type cityInfo struct {
        name     string
        country  string
        timezone string
        lat      string
        lng      string
}

type geocodeInfo struct {
        Results []results
}

type results struct {
        Formatted_address string
        Geometry          struct {
                Location struct {
                        Lat     float64
                        Lng     float64
                }
        }
}

type tzInfo struct {
        RawOffset int64
}

var cityInfos []cityInfo

func parseUTC(city, lan, utc string) {
        tmps := strings.Split(city, "/")
        tmpCity := convertZoneToCity(tmps[len(tmps)-1])
        strs := "&address=" + city + "&language=" + lan
        rets := clientPostRequest(MAP_GOOGLE_GEOCODE + strs)

        infos := geocodeInfo{}
        err := json.Unmarshal(rets, &infos)
        if err != nil {
                fmt.Println("Parse geocode json failed:", err)
                panic(err)
        }

        for _, ret := range infos.Results {
                tzInfo := cityInfo{}
                strs := strings.Split(ret.Formatted_address, ",")
                tzInfo.country = deleteSpace(strs[len(strs)-1])
                tzInfo.name = deleteSpace(tmpCity)
                tzInfo.timezone = fmt.Sprintf("UTC%s", utc)
                tzInfo.lat = formatFloat64(ret.Geometry.Location.Lat)
                tzInfo.lng = formatFloat64(ret.Geometry.Location.Lng)
                fmt.Printf("\"%s\": \"%s (%s) %s\",\n", city,
                        tzInfo.country, tzInfo.timezone, tzInfo.name)
                cityInfos = append(cityInfos, tzInfo)
                break
        }
}

func parseGeocode(city, lan string) {
        strs := "&address=" + city + "&language=" + lan
        //fmt.Println("strs:", strs)

        rets := clientPostRequest(MAP_GOOGLE_GEOCODE + strs)
        //fmt.Println("Geocode post ret:\n", string(rets))

        infos := geocodeInfo{}
        err := json.Unmarshal(rets, &infos)
        if err != nil {
                fmt.Println("Parse geocode json failed:", err)
                panic(err)
        }

        for _, ret := range infos.Results {
                tzInfo := cityInfo{}
                strs := strings.Split(ret.Formatted_address, ",")
                tzInfo.country = deleteSpace(strs[len(strs)-1])
                tzInfo.name = deleteSpace(strs[0])
                //fmt.Println("Address:", strs)
                tzInfo.lat = formatFloat64(ret.Geometry.Location.Lat)
                tzInfo.lng = formatFloat64(ret.Geometry.Location.Lng)
                tz := parseTimezone(tzInfo.lat, tzInfo.lng)
                if tz >= 0 {
                        tzInfo.timezone = fmt.Sprintf("UTC+%d", tz)
                } else {
                        tzInfo.timezone = fmt.Sprintf("UTC%d", tz)
                }

                //fmt.Println(strs, tzInfo.timezone)
                fmt.Printf("%s (%s) %s\n",
                        tzInfo.country, tzInfo.timezone, tzInfo.name)
                cityInfos = append(cityInfos, tzInfo)
        }
}

func deleteSpace(str string) string {
        ret := ""
        l := len(str)
        for i, b := range str {
                //fmt.Println("b:", b, string(b))
                if i == 0 || i == l-1 {
                        if b == ' ' {
                                continue
                        }
                }
                ret += string(b)
        }

        return ret
}

func parseTimezone(lat, lng string) int64 {
        //fmt.Println("lat:", lat, ", lng:", lng)
        rets := clientPostRequest(MAP_GOOGLE_TIMEZONE + "&location=" + lat + "," + lng)
        //fmt.Println("Timezone post ret:", string(rets))

        info := tzInfo{}
        json.Unmarshal(rets, &info)
        //fmt.Println("rawOffset:", info.RawOffset)
        tz := info.RawOffset / 60 / 60
        //fmt.Println("UTC:", tz)

        return tz
}

func clientPostRequest(api string) []byte {
        client := &http.Client{
                CheckRedirect: nil,
        }
        response, err := client.PostForm(api, nil)
        if err != nil {
                fmt.Println("client post failed:", err)
                panic(err)
        }

        if response.StatusCode != 200 {
                fmt.Println("response failed, status:", response.StatusCode)
                panic(response.StatusCode)
        }

        defer response.Body.Close()
        body, _ := ioutil.ReadAll(response.Body)

        return body
}

func formatFloat64(f float64) string {
        return strconv.FormatFloat(f, 'f', -1, 64)
}

func formatInt64(i int64) string {
        return strconv.FormatInt(i, 10)
}

func main() {
        //parseUTC("Havana", "en", "UTC_05")
        //return
        contents, _ := ioutil.ReadFile("zone_utc")
        lines := strings.Split(string(contents), "\n")
        for _, line := range lines {
                if strings.Contains(line, ",") {
                        //fmt.Println("line:", line)
                        strs := strings.Split(line, ",")
                        //parseGeocode(strs[len(strs)-1], "en")
                        parseUTC(strs[0], "en", strs[1])
                        <-time.NewTimer(time.Second * 1).C
                }
        }
        //parseGeocode("Johannesburg", "en")
        //parseTimezone("37.77492950", "-122.41941550")

        /*
           for _, v := range cityInfos {
                   fmt.Println(v.country, "(", v.timezone, ")", v.name)
           }
        */
}

func convertZoneToCity(tz string) string {
        city := ""
        for _, c := range tz {
                if c == '-' || c == '_' {
                        city = city + " "
                } else {
                        city = city + string(c)
                }
        }

        return city
}
